package infra.mongodb;

import domain.model.project.Project;
import domain.model.project.ProjectId;
import domain.model.project.ProjectRepository;
import infra.mongodb.document.MongoDBProjectDocument;
import infra.mongodb.document.MongoDBProjectDocumentImpl;
import infra.mongodb.document.mapper.MongoDBProjectDocumentMapper;
import infra.mongodb.document.serializer.MongoDBProjectDocumentDeserializer;
import infra.mongodb.document.serializer.MongoDBProjectDocumentSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import util.mongodb.MongoDBDataSource;
import util.mongodb.MongoDBDocumentTemplate;
import util.mongodb.SerializationInfo;
import util.mongodb.exception.NoSuchDocumentException;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Gregory Boissinot
 */
public class MongoDBProjectRepository implements ProjectRepository {
    public static final String COLLECTION_NAME = "projects";
    private final MongoDBDocumentTemplate<MongoDBProjectDocument, MongoDBProjectDocumentImpl> mongoDBDocumentTemplate;
    private final MongoDBProjectDocumentMapper mongoDBProjectDocumentMapper;
    private Logger logger = LoggerFactory.getLogger(MongoDBProjectRepository.class);

    public MongoDBProjectRepository(MongoDBDataSource mongoDBDataSource) {
        this.mongoDBProjectDocumentMapper = new MongoDBProjectDocumentMapper();
        this.mongoDBDocumentTemplate =
                new MongoDBDocumentTemplate<MongoDBProjectDocument, MongoDBProjectDocumentImpl>(
                        mongoDBDataSource,
                        getMongoDBProjectDocumentSerializationInfo(),
                        MongoDBProjectDocumentImpl.class,
                        MongoDBProjectRepository.COLLECTION_NAME);
    }

    @Override
    public void store(Project project) {
        logger.debug("storing project", project);
        final MongoDBProjectDocument projectDocument = mongoDBProjectDocumentMapper.toDocument(project);
        mongoDBDocumentTemplate.insert(projectDocument);
    }

    @Override
    public void update(ProjectId projectId, Project newProject) {
        logger.debug("update project", projectId);
        final MongoDBProjectDocument newProjectDocument = mongoDBProjectDocumentMapper.toDocument(newProject);
        mongoDBDocumentTemplate.update(projectId, newProjectDocument);
    }

    @Override
    public Project findById(ProjectId projectId) {
        logger.debug("looking for project", projectId);
        final MongoDBProjectDocument projectDocument = mongoDBDocumentTemplate.findById(projectId);
        return mongoDBProjectDocumentMapper.fromDocument(projectDocument);
    }

    @Override
    public boolean isProjectExist(ProjectId projectId) {
        try {
            findById(projectId);
            logger.debug("project {} exists", projectId);
            return true;
        } catch (NoSuchDocumentException e) {
            return false;
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public List<Project> findAll() {
        List<Project> projects = new ArrayList<Project>();
        final List<MongoDBProjectDocument> projectDocuments = mongoDBDocumentTemplate.findAll();
        logger.debug("found {} projects", projectDocuments.size());
        for (MongoDBProjectDocument projectDocument : projectDocuments) {

            projects.add(mongoDBProjectDocumentMapper.fromDocument(projectDocument));
        }
        return projects;
    }

    private SerializationInfo<MongoDBProjectDocumentImpl> getMongoDBProjectDocumentSerializationInfo() {
        SerializationInfo<MongoDBProjectDocumentImpl> serializationInfo = new SerializationInfo<MongoDBProjectDocumentImpl>();
        serializationInfo.addSerializer(MongoDBProjectDocumentImpl.class, new MongoDBProjectDocumentSerializer());
        serializationInfo.addDeserializer(MongoDBProjectDocumentImpl.class, new MongoDBProjectDocumentDeserializer());
        return serializationInfo;
    }
}
